package tcp

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/injoyai/goutil/conv"
	"gitee.com/injoyai/goutil/net/conn"
	"log"
	"net"
	"strings"
	"sync"
	"time"
)

func NewServer(port ...int) *Server {
	return NewServerWithContext(context.Background(), port...)
}

func NewServerWithContext(ctx context.Context, port ...int) *Server {
	s := &Server{
		port:         conv.GetDefaultInt(10086, port...),
		connMap:      make(map[string]*conn.Conn),
		dataChan:     make(chan *conn.Msg, 1000),
		timeout:      time.Minute * 3,
		readFunc:     conn.DefaultReadFunc,
		dealFunc:     nil,
		closeFunc:    conn.DefaultCloseFunc,
		beforeFunc:   beforeFunc,
		sendFunc:     conn.DefaultSendFunc,
		printFunc:    conn.PrintWithASCII,
		timeoutRead:  conn.DefaultTimeoutRead,
		timeoutSend:  conn.DefaultTimeoutSend,
		timeoutBlack: time.Minute,
	}
	s.ctx, s.cancel = context.WithCancel(ctx)
	return s
}

type Server struct {
	port         int                              //端口
	connMap      map[string]*conn.Conn            //链接集合,远程地址为key
	connMu       sync.RWMutex                     //锁
	debug        bool                             //debug,调试模式
	dataChan     chan *conn.Msg                   //接受数据通道
	readFunc     func(*conn.Conn) ([]byte, error) //数据读取方法
	dealFunc     func(*conn.Msg)                  //数据处理方法
	closeFunc    func(*conn.Msg)                  //断开连接事件
	beforeFunc   func(*conn.Conn) error           //连接前置事件
	err          error                            //错误信息
	sendFunc     func([]byte) []byte              //数据发送函数,包装下原始数据
	printFunc    func(string, *conn.Msg)          //打印数据方法
	timeout      time.Duration                    //超时时间,0是永久有效
	timeoutRead  time.Duration                    //超时时间读取,0不超时
	timeoutSend  time.Duration                    //超时时间發送,0不超时
	timeoutBlack time.Duration                    //黑名单超时断开连接,防止恶意重连
	ctx          context.Context                  //上下文
	cancel       context.CancelFunc               //上下文
	black        map[string]string                //黑名单,黑名单的ip能正常连接,当不处理数据
	blackMu      sync.RWMutex                     //黑名单锁
}

func (this *Server) SetPort(port int) *Server {
	this.port = port
	return this
}

// GetPort 获取端口
func (this *Server) GetPort() int {
	return this.port
}

// IsBlack 是否是黑名单
func (this *Server) IsBlack(ip string) bool {
	this.blackMu.RLock()
	defer this.blackMu.RUnlock()
	_, ok := this.black[ip]
	return ok
}

// GetBlackList 获取黑名单列表
func (this *Server) GetBlackList() (list []string) {
	this.blackMu.RLock()
	defer this.blackMu.RUnlock()
	for _, v := range this.black {
		list = append(list, v)
	}
	return
}

// SetBlack 设置黑名单
// 加入黑名单之后,连接不会马上断开,会保持连接,不做任何处理,
// 等待超时再断开,防止无限重连,占用cpu
func (this *Server) SetBlack(ip string, memo ...string) {
	this.blackMu.Lock()
	defer this.blackMu.Unlock()
	this.black[ip] = strings.Join(memo, ",")
}

// DelBlack 删除黑名单
func (this *Server) DelBlack(ip string) {
	this.blackMu.Lock()
	defer this.blackMu.Unlock()
	delete(this.black, ip)
}

// Debug 调试模式
func (this *Server) Debug(b ...bool) *Server {
	this.debug = !(len(b) > 0 && !b[0])
	return this
}

// Close 退出服务
func (this *Server) Close(err ...error) {
	select {
	case <-this.ctx.Done():
	default:
		this.err = errors.New("主动关闭服务")
		if len(err) > 0 {
			this.err = err[0]
		}
		if this.cancel != nil {
			this.cancel()
		}
		this.CloseConnAll(err...)
	}
}

// SetBeforeFunc 设置连接前置方法,连接数据还未监听
func (this *Server) SetBeforeFunc(fn func(*conn.Conn) error) *Server {
	this.beforeFunc = fn
	return this
}

// SetCloseFunc 设置断开连接事件
func (this *Server) SetCloseFunc(fn func(*conn.Msg)) *Server {
	this.closeFunc = fn
	return this
}

// SetDealFunc 设置处理数据方法
func (this *Server) SetDealFunc(fn func(*conn.Msg)) *Server {
	this.dealFunc = fn
	return this
}

// SetReadFunc 设置数据读取
func (this *Server) SetReadFunc(fn func(*conn.Conn) (bytes []byte, err error)) *Server {
	this.readFunc = fn
	return this
}

// SetSendFunc 设置数据发送函数
func (this *Server) SetSendFunc(fn func([]byte) []byte) *Server {
	this.sendFunc = fn
	return this
}

// SetPrintFunc 设置打印方式
func (this *Server) SetPrintFunc(fn func(string, *conn.Msg)) *Server {
	this.printFunc = fn
	return this
}

// SetPrintWithHEX 设置打印方式HEX
func (this *Server) SetPrintWithHEX() *Server {
	this.printFunc = conn.PrintWithHEX
	return this
}

// SetPrintWithASCII 设置打印方式ASCII
func (this *Server) SetPrintWithASCII() *Server {
	this.printFunc = conn.PrintWithASCII
	return this
}

// SetTimeout 设置超时时间
func (this *Server) SetTimeout(t time.Duration) *Server {
	this.timeout = t
	return this
}

// SetTimeoutRead 设置超时读取时间
func (this *Server) SetTimeoutRead(t time.Duration) *Server {
	this.timeoutRead = t
	return this
}

// SetTimeoutSend 设置超时发送时间
func (this *Server) SetTimeoutSend(t time.Duration) *Server {
	this.timeoutSend = t
	return this
}

// SetTimeoutBlack 设置超时黑名单时间,超过时间会断开连接,否则保持连接,并不做任何处理
func (this *Server) SetTimeoutBlack(t time.Duration) *Server {
	this.timeoutBlack = t
	return this
}

// GetConn 获取一个连接
func (this *Server) GetConn(key string) *conn.Conn {
	this.connMu.RLock()
	defer this.connMu.RUnlock()
	return this.connMap[key]
}

// GetConnOne 获取任意一个连接
func (this *Server) GetConnOne() *conn.Conn {
	this.connMu.RLock()
	defer this.connMu.RUnlock()
	for _, v := range this.connMap {
		return v
	}
	return nil
}

// GetConnAll 获取所有连接
func (this *Server) GetConnAll() map[string]*conn.Conn {
	m := make(map[string]*conn.Conn)
	this.connMu.RLock()
	defer this.connMu.RUnlock()
	for i, v := range this.connMap {
		m[i] = v
	}
	return m
}

// SendConn 给一个链接发送数据
func (this *Server) SendConn(key, msg string) error {
	if c := this.GetConn(key); c != nil {
		return c.Send(msg)
	}
	return errors.New("链接不存在")
}

// SendConnAll 广播,发送数据给所有连接
func (this *Server) SendConnAll(msg string) {
	for _, c := range this.GetConnAll() {
		_ = c.Send(msg)
	}
}

// CloseConn 关闭一个连接
func (this *Server) CloseConn(key string, err ...error) {
	if c := this.GetConn(key); c != nil {
		c.Close(err...)
	}
}

// CloseConnAll 关闭所有连接
func (this *Server) CloseConnAll(err ...error) {
	for _, c := range this.GetConnAll() {
		c.Close(err...)
	}
}

// SetConnKey 重命名key
func (this *Server) SetConnKey(oldConn *conn.Conn, newKey string) {
	newConn := this.GetConn(newKey)
	if newConn != nil {
		if newConn.Conn != oldConn.Conn {
			newConn.Close()
		}
	}
	this.connMu.Lock()
	defer this.connMu.Unlock()
	delete(this.connMap, oldConn.GetKey())
	this.connMap[newKey] = oldConn.SetKey(newKey)
}

func (this *Server) Run() error {
	listen, err := net.Listen("tcp", fmt.Sprintf(":%d", this.port))
	if err != nil {
		log.Println("[错误]", err)
		return err
	}

	log.Printf("开启TCP服务(:%d)成功...\n", this.port)
	go func(ctx context.Context) {
		for {
			select {
			case <-ctx.Done():
				_ = listen.Close()
				return
			case bytes := <-this.dataChan:
				if this.dealFunc != nil {
					this.dealFunc(bytes)
				}
			}
		}
	}(this.ctx)

	for {
		select {
		case <-this.ctx.Done():
			return this.err
		default:
		}

		c, err := listen.Accept()
		if err != nil {
			this.Close(err)
			return this.err
		}

		//判断ip是否在黑名单
		//如果是黑名单,则不做任何处理,一段时间后断开连接,防止恶意无限重连
		ip := conn.NewAddr(c.RemoteAddr()).IP()
		if this.IsBlack(ip) {
			_ = c.SetDeadline(time.Now().Add(this.timeoutBlack))
			continue
		}

		x := conn.NewWithContext(this.ctx, c).
			Debug(this.debug).                //调试模式
			SetReadFunc(this.readFunc).       //读取数据方法
			SetDealFunc(this.dealData).       //数据处理方法
			SetCloseFunc(this.delConn).       //连接关闭方法
			SetTimeout(this.timeout).         //设置超时时间
			SetTimeoutRead(this.timeoutRead). //设置读取超时时间
			SetTimeoutSend(this.timeoutSend). //设置发送超时时间
			SetPrintFunc(this.printFunc).     //设置打印函数
			SetSendFunc(this.sendFunc)        //设置发送函数

		//前置操作
		if this.beforeFunc != nil {
			if err := this.beforeFunc(x); err != nil {
				log.Printf("[错误][%s] %v\n", x.GetKey(), err)
				_ = c.Close()
				continue
			}
		}

		this.connMu.Lock()
		this.connMap[x.GetKey()] = x
		this.connMu.Unlock()
		go x.Run()

	}
}

/*



 */

// beforeFunc 默认前置函数
func beforeFunc(c *conn.Conn) error {
	log.Printf("[信息][%s] 新的TCP连接\n", c.GetKey())
	return nil
}

// delConn 删除连接
func (this *Server) delConn(msg *conn.Msg) {
	if this.debug {
		log.Printf("[错误][%s] %s", msg.Key(), msg)
	}
	if this.closeFunc != nil {
		this.closeFunc(msg)
	}
	this.connMu.Lock()
	defer this.connMu.Unlock()
	oldConn := this.connMap[msg.Key()]
	if oldConn == nil || oldConn.Conn != msg.Conn().Conn {
		//存在新连接上来被关闭的情况,判断是否是老的连接
		return
	}
	delete(this.connMap, msg.Key())
}

// dealData 处理数据
func (this *Server) dealData(msg *conn.Msg) {
	select {
	case <-this.ctx.Done():
	case this.dataChan <- msg:
	}
}
